package main

import (
	"fmt"
	"math"
	"time"
)

// PredictionUpdater 预测曲线更新器
type PredictionUpdater struct {
	storage *PredictionStorage
}

// NewPredictionUpdater 创建预测曲线更新器
func NewPredictionUpdater(storage *PredictionStorage) *PredictionUpdater {
	return &PredictionUpdater{
		storage: storage,
	}
}

// Update30minCurve 更新30分钟预测曲线
func (pu *PredictionUpdater) Update30minCurve(
	currentCurve *PredictionCurve,
	actualUsage float64,
	currentTime time.Time,
	updateRange string,
	updateMethod string,
	reason string,
) (*PredictionCurve, error) {

	logTitle(LogLevelInfo, "更新30分钟预测曲线")
	logMessage(LogLevelInfo, "更新范围: %s\n", updateRange)
	logMessage(LogLevelInfo, "更新方式: %s\n", updateMethod)
	logMessage(LogLevelInfo, "更新原因: %s\n", reason)
	logSeparator(LogLevelInfo, "-", 60)

	// 创建新版本
	newCurve := &PredictionCurve{
		ID:                  generateID(),
		Date:                currentCurve.Date,
		CreatedAt:           currentCurve.CreatedAt,
		UpdatedAt:           time.Now(),
		Version:             currentCurve.Version + 1,
		Prediction30min:     make([]float64, len(currentCurve.Prediction30min)),
		Prediction3h:        make([]float64, len(currentCurve.Prediction3h)),
		NextDayPrediction3h: make([]float64, len(currentCurve.NextDayPrediction3h)),
		UpdateReason:        reason,
		UpdateScenario:      updateRange,
		UpdateRange:         updateRange,
	}

	// 复制原始数据
	copy(newCurve.Prediction30min, currentCurve.Prediction30min)
	copy(newCurve.Prediction3h, currentCurve.Prediction3h)
	copy(newCurve.NextDayPrediction3h, currentCurve.NextDayPrediction3h)

	// 计算当前30分钟索引
	current30minIndex := calculate30minIndex(currentTime)
	currentBlockIndex := current30minIndex / 6

	// 计算预测偏差
	predictedUsage := currentCurve.Prediction30min[current30minIndex]
	deviationRatio := 0.0
	if predictedUsage != 0 {
		deviationRatio = actualUsage / predictedUsage
	} else if actualUsage != 0 {
		deviationRatio = 2.0 // 预测为0但实际有值，设置一个较大的调整系数
	} else {
		deviationRatio = 1.0 // 都为0，不调整
	}

	logMessage(LogLevelInfo, "当前时间: %s\n", currentTime.Format(TimeLayout))
	logMessage(LogLevelInfo, "当前30分钟索引: %d\n", current30minIndex)
	logMessage(LogLevelInfo, "当前3小时块: %d\n", currentBlockIndex)
	logMessage(LogLevelInfo, "实际用水量: %.2f m³\n", actualUsage)
	logMessage(LogLevelInfo, "预测用水量: %.2f m³\n", predictedUsage)
	logMessage(LogLevelInfo, "偏差比例: %.2f\n", deviationRatio)
	logSeparator(LogLevelInfo, "-", 60)

	// 根据更新范围确定更新的索引范围
	var startIdx, endIdx int

	switch updateRange {
	case "current_3h":
		// 只更新当前3小时块
		startIdx = currentBlockIndex * 6
		endIdx = startIdx + 6
		if endIdx > len(newCurve.Prediction30min) {
			endIdx = len(newCurve.Prediction30min)
		}
		logMessage(LogLevelInfo, "更新范围: 当前3小时块 (索引 %d-%d)\n", startIdx, endIdx-1)

	case "6h":
		// 更新未来6小时（当前块 + 下一个块）
		startIdx = currentBlockIndex * 6
		endIdx = startIdx + 12
		if endIdx > len(newCurve.Prediction30min) {
			endIdx = len(newCurve.Prediction30min)
		}
		logMessage(LogLevelInfo, "更新范围: 未来6小时 (索引 %d-%d)\n", startIdx, endIdx-1)

	case "全天":
		// 更新从当前时刻到当天结束
		startIdx = current30minIndex
		endIdx = len(newCurve.Prediction30min)
		logMessage(LogLevelInfo, "更新范围: 全天剩余时段 (索引 %d-%d)\n", startIdx, endIdx-1)

	default:
		return nil, fmt.Errorf("unknown update range: %s", updateRange)
	}

	// 根据更新方式调整预测值
	updatedCount := 0
	for i := startIdx; i < endIdx; i++ {
		oldValue := newCurve.Prediction30min[i]
		var newValue float64

		switch updateMethod {
		case "权重衰减":
			// 权重衰减：距离当前时间越远，调整幅度越小
			distance := float64(i - current30minIndex)
			weight := math.Exp(-distance / 6.0) // 每6个30分钟（3小时）衰减到约37%
			adjustment := (deviationRatio - 1.0) * weight
			newValue = oldValue * (1.0 + adjustment)

		case "全部更新":
			// 全部更新：统一按偏差比例调整
			newValue = oldValue * deviationRatio

		default:
			return nil, fmt.Errorf("unknown update method: %s", updateMethod)
		}

		// 确保非负
		if newValue < 0 {
			newValue = 0
		}

		newCurve.Prediction30min[i] = newValue

		if i < startIdx+3 || i >= endIdx-3 {
			// 只打印前3个和后3个，避免输出过多
			logMessage(LogLevelDebug, "  索引%d: %.2f → %.2f m³ (变化: %+.1f%%)\n",
				i, oldValue, newValue, (newValue-oldValue)/oldValue*100)
		} else if i == startIdx+3 {
			logMessage(LogLevelDebug, "  ... (省略中间值) ...\n")
		}

		updatedCount++
	}

	logSeparator(LogLevelInfo, "-", 60)
	logMessage(LogLevelInfo, "✓ 更新完成，共更新 %d 个30分钟预测值\n", updatedCount)

	return newCurve, nil
}

// Reaggregate3hCurve 重新聚合3小时预测曲线（从30分钟数据）
func (pu *PredictionUpdater) Reaggregate3hCurve(curve *PredictionCurve) error {
	logTitle(LogLevelInfo, "重新聚合3小时预测曲线")

	if len(curve.Prediction30min) != 48 {
		return fmt.Errorf("invalid 30min prediction length: expected 48, got %d", len(curve.Prediction30min))
	}

	// 从30分钟数据聚合到3小时
	for i := 0; i < 8; i++ {
		startIdx := i * 6
		endIdx := startIdx + 6

		sum := 0.0
		for j := startIdx; j < endIdx; j++ {
			sum += curve.Prediction30min[j]
		}

		oldValue := curve.Prediction3h[i]
		curve.Prediction3h[i] = sum

		startHour := i * 3
		endHour := startHour + 3
		logMessage(LogLevelInfo, "块%d [%02d:00-%02d:00]: %.2f → %.2f m³ (变化: %+.2f m³)\n",
			i, startHour, endHour, oldValue, sum, sum-oldValue)
	}

	logSeparator(LogLevelInfo, "=", 60)
	logMessage(LogLevelInfo, "✓ 3小时曲线重新聚合完成\n")

	return nil
}

// calculate30minIndex 计算当前时间对应的30分钟索引
func calculate30minIndex(t time.Time) int {
	hour := t.Hour()
	minute := t.Minute()

	// 每小时有2个30分钟索引
	index := hour * 2
	if minute >= 30 {
		index++
	}

	return index
}

// UpdatePredictionWithDecision 根据偏差分析决策更新预测曲线
func (pu *PredictionUpdater) UpdatePredictionWithDecision(
	currentCurve *PredictionCurve,
	decision *UpdateDecision,
	actualUsage float64,
	currentTime time.Time,
) (*PredictionCurve, error) {

	logTitle(LogLevelInfo, fmt.Sprintf("根据决策更新预测曲线 [%s]", decision.Scenario.String()))

	// 如果不需要更新，直接返回原曲线
	if !decision.ShouldUpdate30min && !decision.ShouldReaggregate3h {
		logMessage(LogLevelInfo, "无需更新预测曲线\n")
		return currentCurve, nil
	}

	var updatedCurve *PredictionCurve
	var err error

	// 1. 更新30分钟曲线
	if decision.ShouldUpdate30min {
		updatedCurve, err = pu.Update30minCurve(
			currentCurve,
			actualUsage,
			currentTime,
			decision.UpdateRange30min,
			decision.UpdateMethod,
			decision.Reason,
		)
		if err != nil {
			return nil, fmt.Errorf("failed to update 30min curve: %w", err)
		}
	} else {
		// 如果不更新30min曲线，复制当前曲线
		updatedCurve = &PredictionCurve{
			ID:                  generateID(),
			Date:                currentCurve.Date,
			CreatedAt:           currentCurve.CreatedAt,
			UpdatedAt:           time.Now(),
			Version:             currentCurve.Version + 1,
			Prediction30min:     make([]float64, len(currentCurve.Prediction30min)),
			Prediction3h:        make([]float64, len(currentCurve.Prediction3h)),
			NextDayPrediction3h: make([]float64, len(currentCurve.NextDayPrediction3h)),
			UpdateReason:        decision.Reason,
			UpdateScenario:      decision.Scenario.String(),
			UpdateRange:         "仅重新聚合3h",
		}
		copy(updatedCurve.Prediction30min, currentCurve.Prediction30min)
		copy(updatedCurve.Prediction3h, currentCurve.Prediction3h)
		copy(updatedCurve.NextDayPrediction3h, currentCurve.NextDayPrediction3h)
	}

	// 2. 重新聚合3小时曲线
	if decision.ShouldReaggregate3h {
		if err := pu.Reaggregate3hCurve(updatedCurve); err != nil {
			return nil, fmt.Errorf("failed to reaggregate 3h curve: %w", err)
		}
	}

	// 3. 保存更新后的预测曲线
	if err := pu.storage.SavePrediction(updatedCurve); err != nil {
		return nil, fmt.Errorf("failed to save updated prediction: %w", err)
	}

	logMessage(LogLevelInfo, "\n")
	logMessage(LogLevelInfo, "✓ 预测曲线更新完成并已保存 (版本 v%d → v%d)\n",
		currentCurve.Version, updatedCurve.Version)

	return updatedCurve, nil
}

// CompareVersions 比较两个版本的预测曲线
func CompareVersions(oldCurve, newCurve *PredictionCurve) {
	logTitle(LogLevelInfo, fmt.Sprintf("版本对比 (v%d vs v%d)", oldCurve.Version, newCurve.Version))

	logMessage(LogLevelInfo, "更新时间: %s → %s\n",
		oldCurve.UpdatedAt.Format(TimeLayout),
		newCurve.UpdatedAt.Format(TimeLayout))
	logMessage(LogLevelInfo, "更新原因: %s → %s\n", oldCurve.UpdateReason, newCurve.UpdateReason)

	logSeparator(LogLevelInfo, "-", 60)

	// 比较3小时预测的变化
	logMessage(LogLevelInfo, "\n3小时预测变化:\n")
	totalChange3h := 0.0
	for i := 0; i < len(oldCurve.Prediction3h) && i < len(newCurve.Prediction3h); i++ {
		oldVal := oldCurve.Prediction3h[i]
		newVal := newCurve.Prediction3h[i]
		change := newVal - oldVal
		changePercent := 0.0
		if oldVal != 0 {
			changePercent = change / oldVal * 100
		}
		totalChange3h += math.Abs(change)

		startHour := i * 3
		endHour := startHour + 3
		if math.Abs(change) > 0.01 {
			logMessage(LogLevelInfo, "  块%d [%02d:00-%02d:00]: %.2f → %.2f m³ (%+.2f m³, %+.1f%%)\n",
				i, startHour, endHour, oldVal, newVal, change, changePercent)
		}
	}

	logSeparator(LogLevelInfo, "-", 60)
	logMessage(LogLevelInfo, "3小时预测总变化量: %.2f m³\n", totalChange3h)

	// 统计30分钟预测的变化
	totalChange30min := 0.0
	changedCount := 0
	for i := 0; i < len(oldCurve.Prediction30min) && i < len(newCurve.Prediction30min); i++ {
		change := math.Abs(newCurve.Prediction30min[i] - oldCurve.Prediction30min[i])
		totalChange30min += change
		if change > 0.01 {
			changedCount++
		}
	}

	logMessage(LogLevelInfo, "30分钟预测总变化量: %.2f m³\n", totalChange30min)
	logMessage(LogLevelInfo, "30分钟预测变化的数据点: %d / %d\n", changedCount, len(oldCurve.Prediction30min))

	logSeparator(LogLevelInfo, "=", 60)
}
